use std::{error::Error, marker::PhantomData, str::FromStr};

use apikit_core::types::Method;
use async_trait::async_trait;
use futures::{
    SinkExt, StreamExt,
    stream::{SplitSink, SplitStream},
};
use reqwest::{Client, Url};
use reqwest_websocket::RequestBuilderExt;
use thiserror::Error;

use crate::param::ParamAppender;

#[async_trait(?Send)]
pub trait WsClient {
    type Error: Error;
    type ParamBuilder;
    type Websocket;

    async fn websocket<Param, SendMessage, ReceiveMessage>(
        &self,
        url: &str,
        method: Method,
        param: Param,
    ) -> Result<TypedWebsocket<Self::Websocket, SendMessage, ReceiveMessage>, Self::Error>
    where
        Param: 'static,
        Param: ParamAppender<Self::ParamBuilder>,
        SendMessage: 'static,
        Self::Websocket: Websocket<SendMessage, ReceiveMessage>;
}

pub trait FromMessage<Message>: Sized {
    type Error: Error + 'static;

    fn from_message(message: Message) -> Result<Self, Self::Error>;
}

pub trait IntoMessage<Message> {
    type Error: Error;

    fn into_message(self) -> Result<Message, Self::Error>;
}

#[async_trait(?Send)]
pub trait MessageSender<SendMessage> {
    type Error: Error;
    type TransMessage;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error>;

    async fn close_websocket(&mut self) -> Result<(), Self::Error>;
}

#[async_trait(?Send)]
pub trait MessageReceiver<ReceiveMessage> {
    type Error: Error;
    type TransMessage;

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>>;
}

pub struct TypedSender<Inner, SendMessage>
where
    Inner: MessageSender<SendMessage>,
{
    pub inner: Inner,
    _marker: PhantomData<SendMessage>,
}

impl<Inner, SendMessage> TypedSender<Inner, SendMessage>
where
    Inner: MessageSender<SendMessage>,
    SendMessage: IntoMessage<Inner::TransMessage>,
{
    pub fn new(inner: Inner) -> Self {
        Self {
            inner,
            _marker: PhantomData,
        }
    }
}

pub struct TypedReceiver<Inner, ReceiveMessage>
where
    Inner: MessageReceiver<ReceiveMessage>,
{
    pub inner: Inner,
    _marker: PhantomData<ReceiveMessage>,
}

impl<Inner, ReceiveMessage> TypedReceiver<Inner, ReceiveMessage>
where
    Inner: MessageReceiver<ReceiveMessage>,
    ReceiveMessage: FromMessage<Inner::TransMessage>,
{
    pub fn new(inner: Inner) -> Self {
        Self {
            inner,
            _marker: PhantomData,
        }
    }
}

#[async_trait(?Send)]
pub trait Websocket<SendMessage, ReceiveMessage> {
    type Error: Error;
    type Sender: MessageSender<SendMessage, TransMessage = Self::TransMessage>;
    type Receiver: MessageReceiver<ReceiveMessage, TransMessage = Self::TransMessage>;
    type TransMessage;
    type CloseCode;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error>;

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>>;

    async fn close_websocket(
        self,
        close_code: Self::CloseCode,
        reason: Option<&str>,
    ) -> Result<(), Self::Error>;

    fn split_websocket(
        self,
    ) -> (
        TypedSender<Self::Sender, SendMessage>,
        TypedReceiver<Self::Receiver, ReceiveMessage>,
    );
}

pub struct TypedWebsocket<Inner, SendMessage, ReceiveMessage>
where
    Inner: Websocket<SendMessage, ReceiveMessage>,
{
    pub inner: Inner,
    _smsg: PhantomData<SendMessage>,
    _rmsg: PhantomData<ReceiveMessage>,
}

impl<Inner, SendMessage, ReceiveMessage> TypedWebsocket<Inner, SendMessage, ReceiveMessage>
where
    Inner: Websocket<SendMessage, ReceiveMessage>,
{
    pub fn new(inner: Inner) -> Self {
        Self {
            inner,
            _smsg: PhantomData,
            _rmsg: PhantomData,
        }
    }
}

#[derive(Debug, Error)]
pub enum ReqwestUpgradeError {
    #[error("invalid method [{0}]")]
    InvalidMethod(String),

    #[error("invalid url [{0}]")]
    InvalidUrl(String),

    #[error("{0}")]
    AppendParamError(Box<dyn Error>),

    #[error(transparent)]
    ReqwestError(#[from] reqwest_eventsource::CannotCloneRequestError),

    #[error(transparent)]
    WebsocketError(#[from] reqwest_websocket::Error),
}

#[async_trait(?Send)]
impl WsClient for Client {
    type Error = ReqwestUpgradeError;
    type ParamBuilder = reqwest::RequestBuilder;
    type Websocket = reqwest_websocket::WebSocket;

    async fn websocket<Param, SendMessage, ReceiveMessage>(
        &self,
        url: &str,
        method: Method,
        param: Param,
    ) -> Result<TypedWebsocket<Self::Websocket, SendMessage, ReceiveMessage>, Self::Error>
    where
        Param: 'static,
        Param: ParamAppender<Self::ParamBuilder>,
        SendMessage: 'static,
        Self::Websocket: Websocket<SendMessage, ReceiveMessage>,
    {
        let builder: reqwest::RequestBuilder = self.request(
            method,
            Url::from_str(url).map_err(|_| ReqwestUpgradeError::InvalidUrl(url.to_string()))?,
        );

        let builder = param
            .append_param(builder)
            .map_err(|e| ReqwestUpgradeError::AppendParamError(Box::new(e)))?;
        let websocket = builder.upgrade().send().await?.into_websocket().await?;

        Ok(TypedWebsocket::new(websocket))
    }
}

#[derive(Debug, Error)]
pub enum ReqwestSendMessageError {
    #[error("{0}")]
    IntoMessage(Box<dyn Error>),

    #[error(transparent)]
    SendError(#[from] reqwest_websocket::Error),
}

#[derive(Debug, Error)]
pub enum ReqwestReceiveMessageError {
    #[error("{0}")]
    FromMessage(Box<dyn Error>),

    #[error(transparent)]
    SendError(#[from] reqwest_websocket::Error),
}

#[derive(Debug, Error)]
pub enum ReqwestWebsocketError {
    #[error("{0}")]
    IntoMessage(Box<dyn Error>),

    #[error("{0}")]
    FromMessage(Box<dyn Error>),

    #[error(transparent)]
    SendError(#[from] reqwest_websocket::Error),
}

#[async_trait(?Send)]
impl<SendMessage> MessageSender<SendMessage>
    for SplitSink<reqwest_websocket::WebSocket, reqwest_websocket::Message>
where
    SendMessage: 'static,
    SendMessage: IntoMessage<reqwest_websocket::Message>,
{
    type Error = ReqwestSendMessageError;
    type TransMessage = reqwest_websocket::Message;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error> {
        let item = message
            .into_message()
            .map_err(|e| ReqwestSendMessageError::IntoMessage(Box::new(e)))?;
        self.send(item).await?;
        Ok(())
    }

    async fn close_websocket(&mut self) -> Result<(), Self::Error> {
        self.close().await?;
        Ok(())
    }
}

#[async_trait(?Send)]
impl<ReceiveMessage> MessageReceiver<ReceiveMessage> for SplitStream<reqwest_websocket::WebSocket>
where
    ReceiveMessage: FromMessage<reqwest_websocket::Message>,
{
    type Error = ReqwestReceiveMessageError;
    type TransMessage = reqwest_websocket::Message;

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>> {
        let message = self.next().await?;
        match message {
            Ok(message) => {
                let message = ReceiveMessage::from_message(message)
                    .map_err(|e| ReqwestReceiveMessageError::FromMessage(Box::new(e)));
                Some(message)
            }
            Err(err) => Some(Err(ReqwestReceiveMessageError::SendError(err))),
        }
    }
}

#[async_trait(?Send)]
impl<SendMessage, ReceiveMessage> Websocket<SendMessage, ReceiveMessage>
    for reqwest_websocket::WebSocket
where
    SendMessage: Send + 'static,
    SendMessage: IntoMessage<reqwest_websocket::Message>,
    ReceiveMessage: FromMessage<reqwest_websocket::Message>,
{
    type Error = ReqwestWebsocketError;
    type CloseCode = reqwest_websocket::CloseCode;
    type Sender = SplitSink<reqwest_websocket::WebSocket, reqwest_websocket::Message>;
    type Receiver = SplitStream<reqwest_websocket::WebSocket>;
    type TransMessage = reqwest_websocket::Message;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error> {
        let item = message
            .into_message()
            .map_err(|e| ReqwestWebsocketError::IntoMessage(Box::new(e)))?;
        self.send(item).await?;
        Ok(())
    }

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>> {
        let message = self.next().await?;
        match message {
            Ok(message) => {
                let message = ReceiveMessage::from_message(message)
                    .map_err(|e| ReqwestWebsocketError::FromMessage(Box::new(e)));
                Some(message)
            }
            Err(err) => Some(Err(ReqwestWebsocketError::SendError(err))),
        }
    }

    async fn close_websocket(
        self,
        close_code: Self::CloseCode,
        reason: Option<&str>,
    ) -> Result<(), Self::Error> {
        self.close(close_code, reason).await?;
        Ok(())
    }

    fn split_websocket(
        self,
    ) -> (
        TypedSender<Self::Sender, SendMessage>,
        TypedReceiver<Self::Receiver, ReceiveMessage>,
    ) {
        let (sender, receiver) = self.split();
        (TypedSender::new(sender), TypedReceiver::new(receiver))
    }
}

#[async_trait(?Send)]
impl<Inner, SendMessage, ReceiveMessage> Websocket<SendMessage, ReceiveMessage>
    for TypedWebsocket<Inner, SendMessage, ReceiveMessage>
where
    Inner: Websocket<SendMessage, ReceiveMessage>,
    SendMessage: 'static,
    SendMessage: IntoMessage<Inner::TransMessage>,
    ReceiveMessage: FromMessage<Inner::TransMessage>,
{
    type Error = Inner::Error;
    type CloseCode = Inner::CloseCode;
    type Sender = Inner::Sender;
    type Receiver = Inner::Receiver;
    type TransMessage = Inner::TransMessage;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error> {
        self.inner.send_message(message).await
    }

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>> {
        self.inner.next_message().await
    }

    async fn close_websocket(
        self,
        close_code: Self::CloseCode,
        reason: Option<&str>,
    ) -> Result<(), Self::Error> {
        self.inner.close_websocket(close_code, reason).await
    }

    fn split_websocket(
        self,
    ) -> (
        TypedSender<Self::Sender, SendMessage>,
        TypedReceiver<Self::Receiver, ReceiveMessage>,
    ) {
        self.inner.split_websocket()
    }
}

#[async_trait(?Send)]
impl<Inner, ReceiveMessage> MessageReceiver<ReceiveMessage> for TypedReceiver<Inner, ReceiveMessage>
where
    Inner: MessageReceiver<ReceiveMessage>,
    ReceiveMessage: FromMessage<Inner::TransMessage>,
{
    type Error = Inner::Error;
    type TransMessage = Inner::TransMessage;

    async fn next_message(&mut self) -> Option<Result<ReceiveMessage, Self::Error>> {
        self.inner.next_message().await
    }
}

#[async_trait(?Send)]
impl<Inner, SendMessage> MessageSender<SendMessage> for TypedSender<Inner, SendMessage>
where
    Inner: MessageSender<SendMessage>,
    SendMessage: 'static,
    SendMessage: IntoMessage<Inner::TransMessage>,
{
    type Error = Inner::Error;
    type TransMessage = Inner::TransMessage;

    async fn send_message(&mut self, message: SendMessage) -> Result<(), Self::Error> {
        self.inner.send_message(message).await
    }

    async fn close_websocket(&mut self) -> Result<(), Self::Error> {
        self.inner.close_websocket().await
    }
}
